/* Copyright (c) 2004 Nordic Semiconductor. All Rights Reserved.
 *
 * The information contained herein is confidential property of 
 * Nordic Semiconductor. The use, copying, transfer or disclosure 
 * of such information is prohibited except by express written
 * agreement with Nordic Semiconductor.
 */ 

/** @file
 * Source code using the PS2 interface in a USB application
 * @author Runar Kjellhaug
 *
 */

#include "app_ps2.h"
#include "wdp_device.h"

//-----------------------------------------------------------------------------
// Internal prototype
//-----------------------------------------------------------------------------

static uint8_t ps2_to_usb(uint8_t ps2_make_key, bool extended_cmd);

//-----------------------------------------------------------------------------
// Function bodies
//-----------------------------------------------------------------------------

static uint8_t dev_id;

uint8_t app_ps2_init()
{  
  uint16_t temp;
  
  COUT_HIGH();
  DOUT_HIGH();

  temp=0xffff;
  while(--temp);
   
  dev_id=ps2_write(PS2_GET_DEVICE_ID);
  ps2_write(0xFF, 1);  // Reset with no returndata

  if(dev_id==PS2_MOUSE_STANDARD) 
  {
    ps2_write(PS2_RESET);
    ps2_write(PS2_RESET);
    ps2_write(PS2_RESET);
  
    ps2_write(PS2_SET_RESOLUTION);
    ps2_write(PS2_8_CNT_MM);
  
    ps2_write(PS2_SET_SCALING_1_1);
    
    ps2_write(PS2_SET_SAMPLE_RATE);
    ps2_write(100,1);
          
    ps2_write(PS2_ENABLE_REPORT);
  }
 
  return dev_id;
}

bool app_ps2_get_and_convert(uint8_t* returndata)
{
  uint8_t ps2_temp_var, usb_keycode, index;
  bool ps2_ext_cmd;
   
  if(ps2_fifo_get_status()==true)                                  
  {  
    returndata[APP_CMD]=APP_USER_INPUT;          

    if(dev_id==PS2_KEYBOARD)
    {
      ps2_temp_var=ps2_get_fifo();
      if(ps2_temp_var==0xE0)
      {
        ps2_ext_cmd=true;                      // If extended command
        ps2_temp_var=ps2_get_fifo();           // Read extended command      
      }
      else
      {
        ps2_ext_cmd=false;
      }  


      if(ps2_temp_var==0xF0)                    // If break command
      {
        usb_keycode = ps2_to_usb(ps2_get_fifo(), ps2_ext_cmd);

        if(224 <= usb_keycode && usb_keycode <= 231)
        {
          ps2_temp_var=(1 << (usb_keycode-224));
          returndata[APP_KEYB_MOD] &= ~ps2_temp_var;
        }
        else
        {
          for(index=0; index<6; index++)
          {
            if(returndata[APP_KEYB_KEYS+index]==usb_keycode)
            {
              returndata[APP_KEYB_KEYS+index]=0;
              break;
            } 
          }
        }
      }
      else                                      // Make command 
      {
        usb_keycode = ps2_to_usb(ps2_temp_var, ps2_ext_cmd);
      
        if(224 <= usb_keycode && usb_keycode <= 231)
        {
          ps2_temp_var=(1 << (usb_keycode-224));
          returndata[APP_KEYB_MOD] |= ps2_temp_var;
        }
        else
        {
          ps2_temp_var = 0;
          
          for(index=0; index<6; index++)
          {
            if(returndata[APP_KEYB_KEYS+index] == usb_keycode)
            {
              ps2_temp_var=1; // Key allready pressed  
            }
          }
      
          if(ps2_temp_var==0)
          {
            for(index=0; index<6; index++) // Not really generic...
            {
              if(returndata[APP_KEYB_KEYS+index]==0)
              {
                returndata[APP_KEYB_KEYS+index]=usb_keycode;
                break;
              } 
            }
          }
        }
      }
      return true;
    }
    else
    {
      returndata[APP_MOUSE_BUTTONS]=ps2_get_fifo(); 
      
      if(!(returndata[APP_MOUSE_BUTTONS]&0x08)) // bit 4 in byte 0 shall always be 1, else out of sync
      {
        return false;
      }
        
      returndata[APP_MOUSE_X_DISP]=ps2_get_fifo(); 
      returndata[APP_MOUSE_Y_DISP]=-ps2_get_fifo(); 
      if(dev_id==PS2_MOUSE_INTELLI)
      {
        returndata[APP_MOUSE_Z_DISP]=-ps2_get_fifo(); // Intelli mouse
      }
      else
      {
        returndata[APP_MOUSE_Z_DISP]=0; // Not intelli mouse
      }
      return true;
    }                
  }
  else
  {
    return false;
  }    
}

uint8_t ps2_to_usb(uint8_t ps2_make_key, bool extended_cmd)
{
  uint8_t ret;
  if(extended_cmd==true)
  {
    switch(ps2_make_key)
    {
      case 0x1F: ret=0xE3; break; // L GUI
      case 0x14: ret=0xE4; break; // R ctrl 
      case 0x27: ret=0xE7; break; // R GUI
      case 0x11: ret=0xE6; break; // R ALT
      case 0x2F: ret=0x65; break; // APP
      case 0x70: ret=0x49; break; // Keyboard insert
      case 0x6C: ret=0x4A; break; // Keyboard home
      case 0x7D: ret=0x4B; break; // Keyboard page up
      case 0x71: ret=0x4C; break; // Keyboard delete
      case 0x69: ret=0x4D; break; // Keyboard end
      case 0x7A: ret=0x4E; break; // Keyboard page down
      case 0x75: ret=0x52; break; // Keyboard up arrow
      case 0x6B: ret=0x50; break; // Keyboard left arrow
      case 0x72: ret=0x51; break; // Keyboard down arrow
      case 0x74: ret=0x4F; break; // Keyboard right arrow
      case 0x4A: ret=0x54; break; // Keypad /  
      case 0x5A: ret=0x58; break; // Keypad enter
      default: ret=0; 
    }
  }
  else
  {
    switch(ps2_make_key) 
    {
      case 0x01: ret=0x42; break; // f9        
      case 0x03: ret=0x3E; break; // f5 
      case 0x04: ret=0x3C; break; // f3       
      case 0x05: ret=0x3A; break; // f1       
      case 0x06: ret=0x3B; break; // f2       
      case 0x07: ret=0x45; break; // f12      

      case 0x09: ret=0x43; break; // f10      
      case 0x0A: ret=0x41; break; // f8       
      case 0x0B: ret=0x3F; break; // f6       
      case 0x0C: ret=0x3D; break; // f4       
      case 0x0D: ret=0x2B; break; // TAB
      case 0x0E: ret=0x35; break; // `        
  
      case 0x11: ret=0xE2; break; // L ALT   
      case 0x12: ret=0xE1; break; // L SHIFT 
      case 0x14: ret=0xE0; break; // L CTRL  
      case 0x15: ret=0x14; break; // q        
      case 0x16: ret=0x1E; break; // 1       
      case 0x1A: ret=0x1D; break; // z       
      case 0x1B: ret=0x16; break; // a 
      case 0x1C: ret=0x04; break; // a       
      case 0x1D: ret=0x1A; break; // w       
      case 0x1E: ret=0x1F; break; // 2       
  
      case 0x21: ret=0x06; break; // c       
      case 0x22: ret=0x1B; break; // x       
      case 0x23: ret=0x07; break; // d       
      case 0x24: ret=0x08; break; // e 
      case 0x25: ret=0x21; break; // 4       
      case 0x26: ret=0x20; break; // 3
   
      case 0x29: ret=0x2C; break; // SPACE
      case 0x2A: ret=0x19; break; // v       
      case 0x2B: ret=0x09; break; // f       
      case 0x2C: ret=0x17; break; // t       
      case 0x2D: ret=0x15; break; // r       
      case 0x2E: ret=0x22; break; // 5       
  
      case 0x31: ret=0x11; break; // n       
      case 0x32: ret=0x05; break; // b       
      case 0x33: ret=0x0B; break; // h       
      case 0x34: ret=0x0A; break; // g       
      case 0x35: ret=0x1C; break; // y       
      case 0x36: ret=0x23; break; // 6       

      case 0x3A: ret=0x10; break; // m       
      case 0x3B: ret=0x0D; break; // j       
      case 0x3C: ret=0x18; break; // u       
      case 0x3D: ret=0x24; break; // 7       
      case 0x3E: ret=0x25; break; // 8       
 
      case 0x41: ret=0x36; break; // ,       
      case 0x42: ret=0x0E; break; // k       
      case 0x43: ret=0x0C; break; // i       
      case 0x44: ret=0x12; break; // o       
      case 0x45: ret=0x27; break; // 0       
      case 0x46: ret=0x26; break; // 9

      case 0x49: ret=0x37; break; // .       
      case 0x4A: ret=0x38; break; // /       
      case 0x4B: ret=0x0F; break; // l       
      case 0x4C: ret=0x33; break; // ;       
      case 0x4D: ret=0x13; break; // p       
      case 0x4E: ret=0x2D; break; // -       
  
      case 0x52: ret=0x34; break; // '      

      case 0x54: ret=0x2F; break; // [      
      case 0x55: ret=0x2E; break; // =      

      case 0x59: ret=0xE5; break; // R SHIFT  
      case 0x5A: ret=0x28; break; // ENTER    
      case 0x5B: ret=0x30; break; // ]        

      case 0x58: ret=0x39; break; // CAPS      
      case 0x5D: ret=0x31; break; // "\"        

      case 0x61: ret=0x64; break; // <>    
      case 0x66: ret=0x2A; break; // BACKSP    
      case 0x69: ret=0x59; break; // kp 1      

      case 0x6B: ret=0x5C; break; // kp 4      
      case 0x6C: ret=0x5F; break; // kp 7      

      case 0x70: ret=0x62; break; // kp 0      
      case 0x71: ret=0x63; break; // kp .      
      case 0x72: ret=0x5A; break; // kp 2      
      case 0x73: ret=0x5D; break; // kp 5      
      case 0x74: ret=0x5E; break; // kp 6      
      case 0x75: ret=0x60; break; // kp 8      
      case 0x76: ret=0x29; break; // ESC       
      case 0x77: ret=0x53; break; // num       
      case 0x78: ret=0x44; break; // f11       
      case 0x79: ret=0x57; break; // kp +      
      case 0x7A: ret=0x5B; break; // kp 3      
      case 0x7B: ret=0x56; break; // kp -      
      case 0x7C: ret=0x55; break; // kp *      
      case 0x7D: ret=0x61; break; // kp 9      

      case 0x83: ret=0x40; break; // f7      
      default: ret=0;
    }
  }  
  return ret;
}
